aboutsummaryrefslogtreecommitdiff
path: root/contrib/wpa/tests/hwsim
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/wpa/tests/hwsim')
-rw-r--r--contrib/wpa/tests/hwsim/.gitignore1
-rw-r--r--contrib/wpa/tests/hwsim/README220
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/as.conf27
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/as2.conf24
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-and-crl-expired.pem90
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-and-crl.pem90
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-incorrect-key.pem28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.derbin0 -> 902 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.pem79
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca-key.pem28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca.derbin0 -> 868 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ca.pem79
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/dh.conf8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/dh2.conf8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/dh_param_3072.pem11
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/dsaparam.pem14
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/eap_user.conf167
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/eap_user_vlan.conf7
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-ca-openssl.cnf111
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-ca.key8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-ca.pem13
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/ec-generate.sh53
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-server.key8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-server.pem53
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-user.key8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec-user.pem52
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-ca.key9
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-ca.pem15
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/ec2-generate.sh67
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-server.key9
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-server.pem58
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.key8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.pem56
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-user.key9
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ec2-user.pem57
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.gsm17
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/ca-and-root.pem160
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/cacert.pem81
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/careq.pem16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt2
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt.attr1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/private/cakey.pem28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/serial1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.pem86
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.req16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem167
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.pem86
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.req16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-server/server_and_ica.pem167
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/ca-and-root.pem160
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/cacert.pem81
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/careq.pem16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt.attr1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/private/cakey.pem28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/serial1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.req16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/iCA-user/user_and_ica.pem166
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/ica-generate.sh87
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/index-revoked.txt8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/index-unknown.txt1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/index.txt8
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-multi-server-cache.derbin0 -> 493 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-req.derbin0 -> 76 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.pem76
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.derbin0 -> 490 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der-invalidbin0 -> 343 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/openssl2.cnf147
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/radius_clients.conf1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/radius_clients_ipv6.conf1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/radius_clients_none.conf4
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt6
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt.attr1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rootCA/serial1
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.pem27
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/rsa3072-generate.sh83
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.pem106
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.req22
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.pem96
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.req16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.pem106
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.req21
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol.csr22
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol.pem102
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol2.csr22
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol2.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-certpol2.pem102
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-eku-client.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-expired.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-expired.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-expired.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-extra.pkcs12bin0 -> 3418 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-long-duration.csr27
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-long-duration.key52
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-long-duration.pem107
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server.pem87
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/server.pkcs12bin0 -> 2549 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha384-server.key40
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha384-server.pem115
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha384-user.key38
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha384-user.pem113
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-ca.key52
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-ca.pem32
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/sha512-generate.sh75
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-server.key45
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-server.pem120
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-user.key44
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/sha512-user.pem119
-rwxr-xr-xcontrib/wpa/tests/hwsim/auth_serv/update.sh181
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.csr16
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.key28
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs830
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8.pkcs5v1529
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.pem85
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.pkcs12bin0 -> 2517 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user.rsa-key27
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user2.pkcs12bin0 -> 3558 bytes
-rw-r--r--contrib/wpa/tests/hwsim/auth_serv/user3.pkcs12bin0 -> 3524 bytes
-rwxr-xr-xcontrib/wpa/tests/hwsim/build.sh83
-rw-r--r--contrib/wpa/tests/hwsim/check_kernel.py31
-rw-r--r--contrib/wpa/tests/hwsim/devdetail.xml47
-rw-r--r--contrib/wpa/tests/hwsim/devinfo.xml7
-rw-r--r--contrib/wpa/tests/hwsim/dictionary.radius20
-rw-r--r--contrib/wpa/tests/hwsim/example-hostapd.config116
-rw-r--r--contrib/wpa/tests/hwsim/example-setup.txt191
-rw-r--r--contrib/wpa/tests/hwsim/example-wpa_supplicant.config160
-rw-r--r--contrib/wpa/tests/hwsim/fst_module_aux.py832
-rw-r--r--contrib/wpa/tests/hwsim/fst_test_common.py97
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.py882
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.vlan2
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.vlan23
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.wlan3.vlan2
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.wlan4.vlan2
-rw-r--r--contrib/wpa/tests/hwsim/hostapd.wpa_psk5
-rw-r--r--contrib/wpa/tests/hwsim/hwsim.py114
-rw-r--r--contrib/wpa/tests/hwsim/hwsim_utils.py246
-rw-r--r--contrib/wpa/tests/hwsim/multi-bss-acs.conf28
-rw-r--r--contrib/wpa/tests/hwsim/multi-bss-iface-per_sta_vif.conf42
-rw-r--r--contrib/wpa/tests/hwsim/multi-bss-iface.conf40
-rw-r--r--contrib/wpa/tests/hwsim/multi-bss.conf21
-rw-r--r--contrib/wpa/tests/hwsim/netlink.py237
-rw-r--r--contrib/wpa/tests/hwsim/nl80211.py357
-rw-r--r--contrib/wpa/tests/hwsim/owe-bss-1.conf12
-rw-r--r--contrib/wpa/tests/hwsim/owe-bss-2.conf16
-rw-r--r--contrib/wpa/tests/hwsim/p2p0.conf3
-rw-r--r--contrib/wpa/tests/hwsim/p2p1.conf3
-rw-r--r--contrib/wpa/tests/hwsim/p2p2.conf3
-rw-r--r--contrib/wpa/tests/hwsim/p2p_utils.py394
-rw-r--r--contrib/wpa/tests/hwsim/pps-mo-1.xml62
-rw-r--r--contrib/wpa/tests/hwsim/radius_das.py47
-rw-r--r--contrib/wpa/tests/hwsim/remotehost.py258
-rwxr-xr-xcontrib/wpa/tests/hwsim/rfkill.py152
-rwxr-xr-xcontrib/wpa/tests/hwsim/run-all.sh162
-rwxr-xr-xcontrib/wpa/tests/hwsim/run-tests.py692
-rwxr-xr-xcontrib/wpa/tests/hwsim/start.sh213
-rwxr-xr-xcontrib/wpa/tests/hwsim/stop.sh80
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_acs.py688
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_ciphers.py1200
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_config.py581
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_csa.py189
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_dynamic.py586
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_eap.py7492
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_ft.py3461
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_hs20.py6496
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_ht.py1644
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_mixed.py101
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_open.py1017
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_params.py972
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_pmf.py1204
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_psk.py3553
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_qosmap.py169
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_roam.py395
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_tdls.py652
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_track.py437
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_vht.py1333
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_vlan.py807
-rw-r--r--contrib/wpa/tests/hwsim/test_ap_wps.py10568
-rw-r--r--contrib/wpa/tests/hwsim/test_authsrv.py262
-rw-r--r--contrib/wpa/tests/hwsim/test_autoscan.py81
-rw-r--r--contrib/wpa/tests/hwsim/test_bgscan.py315
-rw-r--r--contrib/wpa/tests/hwsim/test_cert_check.py312
-rw-r--r--contrib/wpa/tests/hwsim/test_cfg80211.py150
-rw-r--r--contrib/wpa/tests/hwsim/test_connect_cmd.py235
-rw-r--r--contrib/wpa/tests/hwsim/test_dbus.py6093
-rw-r--r--contrib/wpa/tests/hwsim/test_dfs.py767
-rw-r--r--contrib/wpa/tests/hwsim/test_dpp.py6874
-rw-r--r--contrib/wpa/tests/hwsim/test_eap.py602
-rw-r--r--contrib/wpa/tests/hwsim/test_eap_proto.py10377
-rw-r--r--contrib/wpa/tests/hwsim/test_erp.py741
-rw-r--r--contrib/wpa/tests/hwsim/test_ext_password.py112
-rw-r--r--contrib/wpa/tests/hwsim/test_fils.py2411
-rw-r--r--contrib/wpa/tests/hwsim/test_fst_config.py553
-rw-r--r--contrib/wpa/tests/hwsim/test_fst_module.py2825
-rw-r--r--contrib/wpa/tests/hwsim/test_gas.py2053
-rw-r--r--contrib/wpa/tests/hwsim/test_hapd_ctrl.py1071
-rw-r--r--contrib/wpa/tests/hwsim/test_he.py1188
-rw-r--r--contrib/wpa/tests/hwsim/test_hostapd_oom.py173
-rw-r--r--contrib/wpa/tests/hwsim/test_hs20_filter.py205
-rw-r--r--contrib/wpa/tests/hwsim/test_hs20_pps_mo.py43
-rw-r--r--contrib/wpa/tests/hwsim/test_ibss.py601
-rw-r--r--contrib/wpa/tests/hwsim/test_ieee8021x.py531
-rw-r--r--contrib/wpa/tests/hwsim/test_kernel.py128
-rw-r--r--contrib/wpa/tests/hwsim/test_macsec.py890
-rw-r--r--contrib/wpa/tests/hwsim/test_mbo.py613
-rw-r--r--contrib/wpa/tests/hwsim/test_module_tests.py28
-rw-r--r--contrib/wpa/tests/hwsim/test_monitor_interface.py94
-rw-r--r--contrib/wpa/tests/hwsim/test_mscs.py231
-rw-r--r--contrib/wpa/tests/hwsim/test_multi_ap.py363
-rw-r--r--contrib/wpa/tests/hwsim/test_nfc_p2p.py848
-rw-r--r--contrib/wpa/tests/hwsim/test_nfc_wps.py709
-rw-r--r--contrib/wpa/tests/hwsim/test_oce.py185
-rw-r--r--contrib/wpa/tests/hwsim/test_ocv.py1247
-rw-r--r--contrib/wpa/tests/hwsim/test_offchannel_tx.py50
-rw-r--r--contrib/wpa/tests/hwsim/test_owe.py928
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_autogo.py936
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_channel.py1384
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_concurrency.py286
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_device.py552
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_discovery.py871
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_ext.py384
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_grpform.py1185
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_invitation.py195
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_messages.py2143
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_persistent.py676
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_service.py586
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_set.py128
-rw-r--r--contrib/wpa/tests/hwsim/test_p2p_wifi_display.py475
-rw-r--r--contrib/wpa/tests/hwsim/test_p2ps.py1689
-rw-r--r--contrib/wpa/tests/hwsim/test_pasn.py850
-rw-r--r--contrib/wpa/tests/hwsim/test_pmksa_cache.py1267
-rw-r--r--contrib/wpa/tests/hwsim/test_radio_work.py133
-rw-r--r--contrib/wpa/tests/hwsim/test_radius.py1710
-rw-r--r--contrib/wpa/tests/hwsim/test_rfkill.py242
-rw-r--r--contrib/wpa/tests/hwsim/test_rrm.py2142
-rw-r--r--contrib/wpa/tests/hwsim/test_sae.py2722
-rw-r--r--contrib/wpa/tests/hwsim/test_sae_pk.py462
-rw-r--r--contrib/wpa/tests/hwsim/test_scan.py2025
-rw-r--r--contrib/wpa/tests/hwsim/test_sigma_dut.py5264
-rw-r--r--contrib/wpa/tests/hwsim/test_ssid.py127
-rw-r--r--contrib/wpa/tests/hwsim/test_sta_dynamic.py329
-rw-r--r--contrib/wpa/tests/hwsim/test_suite_b.py739
-rw-r--r--contrib/wpa/tests/hwsim/test_tnc.py194
-rw-r--r--contrib/wpa/tests/hwsim/test_wep.py172
-rw-r--r--contrib/wpa/tests/hwsim/test_wext.py254
-rw-r--r--contrib/wpa/tests/hwsim/test_wmediumd.py480
-rw-r--r--contrib/wpa/tests/hwsim/test_wnm.py1984
-rw-r--r--contrib/wpa/tests/hwsim/test_wpas_ap.py905
-rw-r--r--contrib/wpa/tests/hwsim/test_wpas_config.py656
-rw-r--r--contrib/wpa/tests/hwsim/test_wpas_ctrl.py2159
-rw-r--r--contrib/wpa/tests/hwsim/test_wpas_mesh.py2534
-rw-r--r--contrib/wpa/tests/hwsim/test_wpas_wmm_ac.py400
-rw-r--r--contrib/wpa/tests/hwsim/tnc/.gitignore4
-rw-r--r--contrib/wpa/tests/hwsim/tnc/Makefile23
-rw-r--r--contrib/wpa/tests/hwsim/tnc/hostap2_imc.c183
-rw-r--r--contrib/wpa/tests/hwsim/tnc/hostap2_imv.c203
-rw-r--r--contrib/wpa/tests/hwsim/tnc/hostap_imc.c72
-rw-r--r--contrib/wpa/tests/hwsim/tnc/hostap_imv.c66
-rw-r--r--contrib/wpa/tests/hwsim/tnc/tnc_config4
-rw-r--r--contrib/wpa/tests/hwsim/tshark.py124
-rw-r--r--contrib/wpa/tests/hwsim/utils.py314
-rw-r--r--contrib/wpa/tests/hwsim/vm/.gitignore1
-rw-r--r--contrib/wpa/tests/hwsim/vm/README80
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/bisect-run.sh43
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/build-codecov.sh57
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/combine-codecov.sh39
-rw-r--r--contrib/wpa/tests/hwsim/vm/dbus.conf34
-rw-r--r--contrib/wpa/tests/hwsim/vm/example-vm-setup.txt95
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/inside.sh169
-rw-r--r--contrib/wpa/tests/hwsim/vm/kernel-config175
-rw-r--r--contrib/wpa/tests/hwsim/vm/kernel-config.uml131
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/parallel-vm.py669
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/process-codecov.sh36
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/uevent.sh9
-rwxr-xr-xcontrib/wpa/tests/hwsim/vm/vm-run.sh202
-rw-r--r--contrib/wpa/tests/hwsim/w1fi_logo.pngbin0 -> 7549 bytes
-rw-r--r--contrib/wpa/tests/hwsim/wlantest.py277
-rw-r--r--contrib/wpa/tests/hwsim/wpasupplicant.py1649
-rw-r--r--contrib/wpa/tests/hwsim/wps-ctrl-credbin0 -> 67 bytes
-rw-r--r--contrib/wpa/tests/hwsim/wps-ctrl-cred2bin0 -> 59 bytes
-rw-r--r--contrib/wpa/tests/hwsim/wps-mixed-credbin0 -> 112 bytes
-rw-r--r--contrib/wpa/tests/hwsim/wps-wep-credbin0 -> 53 bytes
303 files changed, 144329 insertions, 0 deletions
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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.der
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca.der
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-multi-server-cache.der
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-req.der
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-extra.pkcs12
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server.pkcs12
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.pkcs12
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user2.pkcs12
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user3.pkcs12
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/w1fi_logo.png
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wps-ctrl-cred
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wps-ctrl-cred2
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wps-mixed-cred
Binary files 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
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wps-wep-cred
Binary files differ